<?php

namespace App\Libraries\Payment\KylinCloud;


use App\Exceptions\TransactionException;
use App\Exceptions\WithdrawException;
use App\Libraries\Payment\PaymentInterface;
use App\Models\Setting;
use App\Services\Withdraw\UserWithdrawService;
use GuzzleHttp\Client;
use GuzzleHttp\Exception\GuzzleException;
use GuzzleHttp\Psr7\Request;
use GuzzleHttp\Psr7\Response;
use Illuminate\Support\Facades\Log;

class KylinCommonRequest
{
    public string $bizContent;
    public string $charset;
    public string $companyId;
    public string $signType;
    public string $timestamp;
    public string $sign;
}

class KylinTransferRequest
{
    // @NOTICE: FAKED
    public string $name = '无名氏';
    public string $accountType;
    public string $accountNo;

    // @NOTICE: FAKED
    public string $idCardNo = '431022199811158056';
    public string $transAmount;

    // @NOTICE: FAKED
    public string $phone = '13109273645';
    public string $notifyUrl;
    public string $remark;
}


class KylinPayment implements PaymentInterface
{
    const TRANSFER_REQUEST_API = 'https://api.kylin-task.com/api/kycloud/open/v1.0/transfer';

    /**
     * @throws TransactionException
     * @throws GuzzleException
     * @throws WithdrawException
     */
    public static function makeRequest(array $params): string
    {
        $bank = Setting::get('bank');
        UserWithdrawService::checkAccountBank($params['transAmount']);

        if (env('PAYMENT_SANDBOX')) {
            return self::fakeRequest($params);
        }

        $transferRequest = new KylinTransferRequest();
        $transferRequest->accountType = $params['accountType'] ?? '';
        $transferRequest->accountNo = $params['accountNo'] ?? '';
        $transferRequest->outBizNo = $params['outBizNo'];
        $transferRequest->transAmount = $params['transAmount'];
        $transferRequest->notifyUrl = $params['notifyUrl'];
        $fixted = Setting::get('payment')['kylin']['remark'] ?? '';
        if (empty($fixted)) {
            $fixted = 'JS02#剧上头服务费';
        }
        $fixted .= '_' . $bank['account_id'];
        $transferRequest->remark = $fixted . $params['remark'];

        $content = json_encode($transferRequest, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE);

        $commonRequest = new KylinCommonRequest();
        $commonRequest->bizContent = $content;
        $commonRequest->charset = 'utf-8';
        $commonRequest->companyId = Setting::get('payment')['kylin']['company_id'];
        $commonRequest->signType = Setting::get('payment')['kylin']['sign_type'];
        $commonRequest->timestamp = date('Y-m-d H:i:s', time());

        $commonRequestToSign = "bizContent=$content&charset=$commonRequest->charset&companyId=$commonRequest->companyId&signType=$commonRequest->signType&timestamp=$commonRequest->timestamp";
        $sign = self::rsa2Sign($commonRequestToSign);
        $commonRequest->sign = $sign;

        $transferPayload = json_encode($commonRequest, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE);

        $client = new Client();
        $headers = [
            'Content-type' => 'application/json',
        ];
        $request = new Request('POST', self::TRANSFER_REQUEST_API, $headers, $transferPayload);
        /** @var Response $result */
        $result = $client->send($request);
        $raw = $result->getBody()->getContents();

        Log::channel('kylin')->debug([
            'url' => self::TRANSFER_REQUEST_API,
            'post' => $transferPayload,
            'response' => $raw
        ]);

        $content = (array)json_decode($raw, true);
        if (empty($content)) {
            throw new TransactionException($raw);
        }
        if (!($content['success'] || false) || empty($content['object'])) {
            throw new TransactionException($content['message'] ?? ($content['code'] ?? ''));
        }

        $orderId = $content['object']['orderId'] ?? '';
        if (empty($orderId)) {
            throw new TransactionException($content['message'] ?? '转账异常');
        }

        return $orderId;
    }

    // @notice: test only.
    public static function fakeRequest(array $params): string
    {
        $orderId = strtoupper(uniqid());
        Log::channel('sandbox')->debug([
            'orderId' => $orderId,
            'params' => $params,
        ]);

        return $orderId;
    }

    protected static function rsa2Sign(string $data)
    {
        $privateKey = Setting::get('payment')['kylin']['private_key'];
        $privateKey = "-----BEGIN RSA PRIVATE KEY-----
{$privateKey}
-----END RSA PRIVATE KEY-----";

        $res = openssl_pkey_get_private($privateKey);
        // 使用私钥计算签名
        openssl_sign($data, $signature, $res, "SHA256");

        return base64_encode($signature);
    }
}
